package net.j4love.nio.channels;

import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Objects;

/**
 * @author he peng
 * @create 2018/6/4 18:14
 * @see
 */
public class SocketChannelTest {

    final SocketAddress socketAddress = new InetSocketAddress("127.0.0.1" , 9999);

    public SocketChannelTest() throws IOException {}


    @Test
    public void openClientTest0() throws Exception {
        SocketChannel sChannel = SocketChannel.open();
        sChannel.configureBlocking(false);
        Selector selector = Selector.open();
        sChannel.register(selector , SelectionKey.OP_CONNECT);
        boolean connected = sChannel.connect(socketAddress);
        try {
            while (sChannel.isOpen() && selector.isOpen()) {
                selector.select();

                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    iterator.remove();

                    if (! sk.isValid()) {
                        System.out.println("selection key is invalid");
                        continue;
                    } else if (sk.isAcceptable()) {
                        System.out.println("selection key is Acceptable");
                    } else if (sk.isConnectable()) {
                        System.out.println("selection key is Connectable");
                        if (! connected) {
                            SocketChannel channel = (SocketChannel) sk.channel();
                            if (! channel.isConnected()) {
                                channel.finishConnect();
                                channel.register(selector , SelectionKey.OP_WRITE);
                                System.out.println("connect finished");
                            }
                        }
                    } else if (sk.isReadable()) {
                        System.out.println("selection key is Readable");
                        SocketChannel channel = (SocketChannel) sk.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
                        sChannel.read(buffer);
                        buffer.flip();
                        System.out.println("receive from server message -> " +
                                new String(buffer.array() , 0 , buffer.limit() , Charset.forName("utf-8")) +
                                " ,time -> " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                        channel.register(selector , SelectionKey.OP_WRITE);
                    } else if (sk.isWritable()) {
                        System.out.println("selection key is Writable");
                        SocketChannel channel = (SocketChannel) sk.channel();
                        if (channel.isConnected()) {
                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                            buffer.put("hello server".getBytes());
                            buffer.flip();
                            channel.write(buffer);
                            channel.register(selector , SelectionKey.OP_READ);
                        }
                    } else {
                        throw new IllegalStateException("Unknown readyOps");
                    }
                }
            }
        } finally {
            sChannel.close();
            selector.close();
        }

    }


    @Test
    public void openServerTest0() throws Exception {
        Selector selector = Selector.open();
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        ssChannel.configureBlocking(false);
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        ssChannel.bind(socketAddress);
        System.out.println("server start in " + socketAddress);
        try {
            while (ssChannel.isOpen() && selector.isOpen()) {

                System.out.println("Select .......");
                int selectedNum = selector.select();
                // select() 返回值不表示选中的处于就绪状态的通道数量，而是处于就绪状态的通道中就绪状态已经更新的通道数量 ，
                // 所以不能以这个返回值是否为 0 来判断是否有处于就绪状态的通道被选中

                /*if (selectedNum == 0) {
                    continue;
                }*/


                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                   SelectionKey sk = iterator.next();
                   iterator.remove();

                   if (! sk.isValid()) {
                        try {
                            System.out.println("selection key is invalid");
                            continue;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else if (sk.isAcceptable()) {
                        try {
                            System.out.println("selection key is Acceptable");
                            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) sk.channel();
                            SocketChannel sChannel = serverSocketChannel.accept();
                            if (Objects.nonNull(sChannel)) {
                                sChannel.configureBlocking(false);
                                sChannel.register(selector , SelectionKey.OP_READ);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else if (sk.isConnectable()) {
                        System.out.println("selection key is Connectable");

                    } else if (sk.isReadable()) {
                        try {
                            SocketChannel sChannel = (SocketChannel) sk.channel();
                            System.out.println("selection key is Readable");
                            ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
                            sChannel.read(buffer);
                            buffer.flip();
                            System.out.println("receive from client (" + sChannel.getRemoteAddress() + ") message -> " +
                                    new String(buffer.array() , 0 , buffer.limit() , Charset.forName("utf-8")) +
                                    " ,time -> " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                            sChannel.register(selector , SelectionKey.OP_WRITE);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else if (sk.isWritable()) {
                        try {
                            SocketChannel channel = (SocketChannel) sk.channel();
                            System.out.println("selection key is Writable");
                            if (channel.isConnected()) {
                                ByteBuffer buffer = ByteBuffer.allocate(1024);
                                buffer.put("hello client".getBytes());
                                buffer.flip();
                                channel.write(buffer);
                                channel.register(selector , SelectionKey.OP_READ);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        throw new IllegalStateException("Unknown readyOps");
                    }
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            ssChannel.close();
            selector.close();
        }
    }
}
